//=============================================================================
/**
 *  @file    Logger.cpp
 *
 *  ver 1.0.0 2004/12/01 Naven Exp, for Fast Logger Framework.
 *
 *  @author Naven (Navy Chen) 2004/12/01 created.
 */
//=============================================================================

#include "logger.h"
#include "datetime.h"

#include <errno.h>
#include <errno.h>
#include <fcntl.h>
#include <fstream>
#include <iostream>
#include <linux/unistd.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/types.h>

#define MAXLOGRECSIZE 1024

static int g_init_module = 0;
static int g_init_path = 0;
pid_t gettid()
{
    return syscall(SYS_gettid);
}

//===========Logger Functions Implement============

Logger::Logger()
: mregMux(), mLogSwitch(), mModuleName(), mStorePath(), mFileHandle(0), mFileName(""),
  mLogBySize(0), mFileSeq(0), mSplitTime(true)
{
    mLogSwitch = 0xFF;
}
Logger::~Logger()
{
    closeFile();
    mLogSwitch = 0x00;
}

/**
 *const LogLevel EMERG_LOG_LEVEL   = 0;
 *const LogLevel ALERT_LOG_LEVEL   = 1;
 *const LogLevel CRIT_LOG_LEVEL    = 2;
 *const LogLevel ERROR_LOG_LEVEL   = 3;
 *const LogLevel WARN_LOG_LEVEL    = 4;
 *const LogLevel NOTICE_LOG_LEVEL  = 5;
 *const LogLevel INFO_LOG_LEVEL    = 6;
 *const LogLevel FATAL_LOG_LEVEL   = 7;
 *const LogLevel OFF_LOG_LEVEL     = 8;
 *const LogLevel TRACE_LOG_LEVEL   = 10;
 */
// log level switch type
// EMERG_LOG_LEVEL 0000 0001
// ALERT_LOG_LEVEL 0000 0010
// CRIT_LOG_LEVEL  0000 0100
// ERROR_LOG_LEVEL 0000 1000
// WARN_LOG_LEVEL  0001 0000
// NOTICE_LOG_LEVEL 0010 0000
// INFO_LOG_LEVEL  0100 0000
// FATAL_LOG_LEVEL 1000 0000
// ALL_LOG_LEVEL   1111 1111
void Logger::disableLogLevel(LogLevel ll)
{
    switch (ll) {
        case 0:
            mLogSwitch &= 0xFE;
            break;
        case 1:
            mLogSwitch &= 0xFD;
            break;
        case 2:
            mLogSwitch &= 0xFB;
            break;
        case 3:
            mLogSwitch &= 0xF7;
            break;
        case 4:
            mLogSwitch &= 0xEF;
            break;
        case 5:
            mLogSwitch &= 0xDF;
            break;
        case 6:
            mLogSwitch &= 0xBF;
            break;
        case 7:
            mLogSwitch &= 0x7F;
            break;
        case 8:
            mLogSwitch &= 0x00;
            break;
        default:
            break;
    }
}
void Logger::enableLogLevel(LogLevel ll)
{
    switch (ll) {
        case 0:
            mLogSwitch |= 0x01;
            break;
        case 1:
            mLogSwitch |= 0x02;
            break;
        case 2:
            mLogSwitch |= 0x04;
            break;
        case 3:
            mLogSwitch |= 0x08;
            break;
        case 4:
            mLogSwitch |= 0x10;
            break;
        case 5:
            mLogSwitch |= 0x20;
            break;
        case 6:
            mLogSwitch |= 0x40;
            break;
        case 7:
            mLogSwitch |= 0x80;
            break;
        case 9:
            mLogSwitch |= 0xFF;
            break;
        default:
            break;
    }
}

static void getcurtime(char *timebuf)
{
    CDateTime datetime;
    sprintf(timebuf, "%04d%02d%02d%02d", datetime.Year(), datetime.Month(),
            datetime.Day(), datetime.Hour());
}

void Logger::setStorePath(const char *storepath)
{
    if (g_init_path > 0)
        return;
    mregMux.lock();
    g_init_path++;
    mregMux.unlock();
    sprintf(mStorePath, "%s", storepath);
}

void Logger::setLogFileBySize(long fileMaxSize)
{
    mLogBySize = fileMaxSize;
}

void Logger::setLogSplitByHout(bool bSplit)
{
    mSplitTime = bSplit;
}

void Logger::setModuleName(const char *modulename)
{
    if (g_init_module > 0)
        return;
    mregMux.lock();
    g_init_module++;
    mregMux.unlock();
    sprintf(mModuleName, "%s", modulename);
}

void Logger::getLogFileName(std::string &logname)
{
    if (mLogBySize > 0) {
        struct stat tmp;
        memset(&tmp, 0, sizeof(tmp));
        stat(mFileName.c_str(), &tmp);
        if (tmp.st_size < mLogBySize) {
            if (mFileName.empty()) {
                char buffer[10] = { 0 };
                sprintf(buffer, "%d", mFileSeq);
                logname.append("_").append(buffer);
                mFileSeq++;
            } else {
                logname = mFileName;
            }
        } else {
            if (string::npos == mFileName.find(logname)) {
                mFileSeq = 0;
            }
            char buffer[10] = { 0 };
            sprintf(buffer, "%d", mFileSeq);
            logname.append("_").append(buffer);
            mFileSeq++;
        }
    }
}
/*void Logger::writelog(LogLevel ll,
        int errnum,
        const char *format,
        ...)*/
void Logger::writelog(const char *fileName, int line, const char *function, LogLevel ll, int errnum, const char *format, ...)
{
    if (ll >= 0 && ll < 8 && ((int)((mLogSwitch >> ll) & 0x01) == 1)) {
        char timebuf[32] = { 0 };
        getcurtime(timebuf);

        char msg[MAXLOGRECSIZE + 1] = { 0 };

        memset(msg, 0, MAXLOGRECSIZE + 1);
        va_list ap;
        memset(&ap, 0, sizeof(va_list));
        va_start(ap, format);
        vsnprintf(msg, MAXLOGRECSIZE, format, ap);
        va_end(ap);

        CDateTime datetime;

        char temp[1024 + 1024] = { 0 };

        if (strlen(mModuleName) == 0) {
            sprintf(mModuleName, "%s", "main");
        }

        string level = "";
        switch (ll) {
            case 0:
                level = "EMERG";
                break;
            case 1:
                level = "ALERT";
                break;
            case 2:
                level = "CRIT";
                break;
            case 3:
                level = "ERROR";
                break;
            case 4:
                level = "WARNING";
                break;
            case 5:
                level = "NOTICE";
                break;
            case 6:
                level = "INFO";
                break;
            case 7:
                level = "DEBUG";
                break;
            default:
                level = "";
                break;
        }

        sprintf(temp, "%s %s (pid=%d) (%s) (errno=%d) %s(%d) %s [%s]\n",
                datetime.LongDateTime().c_str(), mModuleName, gettid(), level.c_str(),
                errnum, fileName, line, function, msg);

        string logname = string("");
        if (mSplitTime) {
            logname = string(mStorePath) + string("/") + string(mModuleName) +
                      string("_") + string(timebuf) + string(".log");
        } else {
            logname =
            string(mStorePath) + string("/") + string(mModuleName) + string(".log");
        }
        mregMux.lock();
        getLogFileName(logname);
        if (0 != logname.compare(mFileName)) {
            closeFile();
            mFileName = logname;
            if (!openFile(mFileName.c_str())) {
                mregMux.unlock();
                return;
            }
        }

        if (mFileHandle > 0) {
            write(mFileHandle, temp, strlen(temp));
        }
        mregMux.unlock();
    }
}

void Logger::closeFile()
{
    if (mFileHandle > 0) {
        close(mFileHandle);
        mFileHandle = 0;
    }
}
bool Logger::openFile(const char *file)
{
    if (NULL != file) {
        if (mFileHandle > 0) {
            closeFile();
        }
        if (0 >= (mFileHandle = open(file, O_WRONLY | O_APPEND | O_CREAT, 0666))) {
            return false;
        }
        return true;
    }
    return false;
}
